﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using xKnight.Attacking.EncodedXss;
using xKnight.Attacking.SimpleXss;
using xKnight.Data;
using xKnight.Models;

namespace xKnight.Attacking
{

    public delegate void AttackCompletedEventHandler(object sender, AttackCompletedEventArgs e);

    public delegate void AttackStartedEventHandler(object sender, AttackStartedEventArgs e);

    public delegate void AttackAnnouncedEventHandler(object sender, AttackAnnouncedEventArgs e);

    public sealed class AttackManager
    {
        #region Events

        /// <summary>
        /// This event will rise just after all attackers done their job
        /// </summary>
        public event AttackCompletedEventHandler AttackCompleted;

        /// <summary>
        /// This event will rise just after all attackers get started
        /// </summary>
        public event AttackStartedEventHandler AttackStarted;
        
        /// <summary>
        /// this event will rise and report about attack status
        /// </summary>
        public event AttackAnnouncedEventHandler AttackAnnounced;

        #endregion
        
        #region Fields
      
        private int _crawlId; // determine to attack to what!
        private int _numberOfAgentsPerAttack; // number of agents for each type of attack
        private int _numberOfAttackers; // Total number of attackers. One Attacker for each type of attack
        private int _aliveAttackers;
        private object _lock = new object(); // used for managing shared resources in critical sections
        private AttackType _attackType;

        #endregion

        #region Constructors

        public AttackManager(int crawlId,int numberOfAgentsPerAttack,AttackType attackType)
        {
            _attackType = attackType;
            _numberOfAgentsPerAttack = numberOfAgentsPerAttack;
            _crawlId=crawlId;
        }

        #endregion

        #region Interface

        public void Attack()
        {
            //extract hosts from CrawlSetting Entity
            Host[] hosts = DataLayer.GetHosts(new WebCrawlingSetting() { Id=_crawlId});

            AttackType[] types = AttackerFactory.GetAllAttackTypes();

            foreach (var attackType in types)
            {
                if (_attackType.HasFlag(attackType))
                {
                    Attacker attacker = CreateAttackerForHosts(attackType, hosts);
                    _numberOfAttackers++;
                    attacker.Attack();
                }
            }
        }

        #endregion

        #region EventHandler

        void Attacker_AttackStarted(object sender, AttackStartedEventArgs e)
        {
            lock (_lock)
            {
                IncrementAliveAttackers();
                if (AllAtackersStarted())
                {
                    OnAttackStarted(e.AttackId);
                }
            }
        }


        void Attacker_AttackCompleted(object sender, AttackCompletedEventArgs e)
        {
            lock (_lock)
            {
                DecrementAliveAttackers();
                if (!HasAliveAttacker())
                {
                    OnAttackCompleted(e.AttackId);
                }
            }
        }

        void Attacker_AttackAnnounced(object sender, AttackAnnouncedEventArgs e)
        {
            OnAttackAnnounced(e);
        }

        #endregion

        #region Events Raiser

        private void OnAttackCompleted(int attackId)
        {
            if (AttackCompleted != null)
            {
                AttackCompleted(this, new AttackCompletedEventArgs(attackId,DateTime.Now));
            }
        }

        private void OnAttackStarted(int attackId)
        {
            if (AttackStarted != null)
            {
                AttackStarted(this, new AttackStartedEventArgs(attackId, DateTime.Now));
            }
        }

        private void OnAttackAnnounced(AttackAnnouncedEventArgs e)
        {
            if (AttackAnnounced != null)
            {
                AttackAnnounced(this, new AttackAnnouncedEventArgs(e.AnnounceItem));
            }
        }

        #endregion

        #region private Methods

        private Attacker CreateAttackerForHosts(AttackType attackType, Host[] hosts)
        {
            Attack attack = new Attack();
            attack.WebCrawlingSettingId = _crawlId;
            attack.StartTime = DateTime.Now;
            attack.AttackType = (int)attackType;

            DataLayer.Save(attack);

            Attacker attacker = AttackerFactory.CreateAttacker(attack, _numberOfAgentsPerAttack, hosts);
            attacker.AttackAnnounced += Attacker_AttackAnnounced;
            attacker.AttackCompleted += Attacker_AttackCompleted;
            attacker.AttackStarted += Attacker_AttackStarted;

            return attacker;
        }

        private bool HasAliveAttacker()
        {
            return _aliveAttackers != 0;
        }

        private void DecrementAliveAttackers()
        {
            _aliveAttackers--;
        }

        private bool AllAtackersStarted()
        {
            return _aliveAttackers == _numberOfAttackers;
        }

        private void IncrementAliveAttackers()
        {
            _aliveAttackers++;
        }

        #endregion
    }
}
